﻿/*
 * Copyright (C) 2012 Travis Sharp - travis@inhumanstudios.com
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 * 
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Timers;
using System.Xml.Serialization;

namespace IS.App
{
    public partial class AnyService : ServiceBase
    {
        private bool _isMgmtSvc = false;
        private volatile Process _currentProcess = null;
        private System.Threading.Timer _serviceTimer = null;
        private ServiceConfig _config = null;

        public AnyService()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Retrieve the working directory of current executing service, create directory if it doesnt already exist.
        /// </summary>
        /// <returns></returns>
        private string GetWorkingDirectory()
        {
            string baseServicesDir = String.Format("{0}\\Services", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
            if (!Directory.Exists(baseServicesDir))
            {
                Directory.CreateDirectory(baseServicesDir);
            }

            string baseDir = String.Format("{0}\\{1}", baseServicesDir, this.ServiceName);
            if (!Directory.Exists(baseDir))
            {
                Directory.CreateDirectory(baseDir);
            }
            
            return baseDir;
        }

        /// <summary>
        /// Specifies whether or not this service should run in management mode.
        /// </summary>
        public bool IsManagementSvc
        {
            get
            {
                return this._isMgmtSvc;
            }
            set
            {
                this._isMgmtSvc = value;
            }
        }

        protected override void OnStart(string[] args)
        {
            try
            {                
                // Get the root execution path for the service                
                string rootExecutionPath =
                    System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
                // Set the current directory to the installation directory                
                Directory.SetCurrentDirectory(rootExecutionPath);

                StartEmbeddedService();

                TimerCallback timerDelegate = new TimerCallback(ServicePulse);                
                _serviceTimer = new System.Threading.Timer(timerDelegate, null, new TimeSpan(0, 0, 0), new TimeSpan(0, 0, 5));                        
            }            
            catch (Exception ex)            
            {                
                string msg = (ex.Message != null ? ex.Message : string.Empty)                    
                    + (ex.InnerException != null ? (ex.InnerException.Message != null ? ex.InnerException.Message : string.Empty) : string.Empty);                
                throw new Exception(String.Format("{0} - OnStart: {1}", this.ServiceName, msg), ex);            
            }
        }

        private void StartEmbeddedService()
        {
            Directory.SetCurrentDirectory(GetWorkingDirectory());

            string cfgFile = String.Format("{0}_config.xml", this.ServiceName);
            if (!File.Exists(cfgFile))
            {
                using (Stream fs = File.Open(cfgFile, FileMode.Create, FileAccess.Write, FileShare.Read))
                {
                    XmlSerializer ser = new XmlSerializer(typeof(ServiceConfig));
                    ser.Serialize(fs, new ServiceConfig()
                    {
                        Executable = "",
                        Arguments = new string[]{}
                    });

                    fs.Flush();
                }
            }

            using (Stream fs = File.Open(cfgFile, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                XmlSerializer ser = new XmlSerializer(typeof(ServiceConfig));
                this._config = (ServiceConfig)ser.Deserialize(fs);
            }

            if (String.IsNullOrEmpty(this._config.Executable) || 
                !File.Exists(this._config.Executable))
            {
                throw new Exception(String.Format("{0}, the service executable, does not exist, unable to start service.", this._config.Executable));
            }

            ProcessStartInfo startInfo = new ProcessStartInfo()
            {
                FileName = _config.Executable,
                Arguments = String.Join(" ", _config.Arguments),
                WorkingDirectory = GetWorkingDirectory(),
                CreateNoWindow = true
            };

            _currentProcess = Process.Start(startInfo);
        }

        private void ServicePulse(object state)
        {
            _serviceTimer.Change(-1, -1);

            if (_currentProcess == null || _currentProcess.HasExited)
            {
                this.Stop();
            }

            _serviceTimer.Change(5000, 5000);
        }

        protected override void OnStop()
        {
            // Kill the process if need be
            if (!_currentProcess.HasExited)
            {
                _currentProcess.Kill();
            }
        }
    }
}
